// Copyright (c) 2023 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.


#pragma once

#include <cstdint>
#include <stddef.h>
#include <type_traits>


static_assert(__cplusplus >= 201703L, "C++ 17 or later");


namespace data_basic {

    namespace hide {
        template <bool fit_in_8, bool fit_in_16, bool fit_in_32>
        struct enough_index_type {};


        template <>
        struct enough_index_type<true, true, true> {
            using type = uint8_t;
        };


        template <>
        struct enough_index_type<false, true, true> {
            using type = uint16_t;
        };


        template <>
        struct enough_index_type<false, false, true> {
            using type = uint32_t;
        };


        template <>
        struct enough_index_type<false, false, false> {
            using type = uint64_t;
        };
    }  // namespace hide


    /**
     * @breif 根据所需的bit flag 数量，选择最小能容纳的整数类型
     *
     * 若所需数量为8，则可用uint8_t，若数量为10，则可用uint16_t，依此类推。
     *
     */
    template <size_t Count>
    struct enough_bit_flag {
        using type = typename hide::enough_index_type<Count <= 8, Count <= 16, Count <= 32>::type;
    };

    template <size_t Count>
    using enough_bit_flag_t = typename enough_bit_flag<Count>::type;


    /**
     * @breif 根据所需索引的元素数量，选择最小能容纳的整数类型
     *
     * 若列表最多元素数量为100，则可用uint8_t 索引其中每个元素；若元素数量为1000，则需要用uint16_t。
     *
     */
    template <size_t Count>
    struct enough_index {
        using type = typename hide::enough_index_type<Count <= UINT8_MAX, Count <= UINT16_MAX, Count <= UINT64_MAX>::type;
    };


    template <size_t Count>
    using enough_index_t = typename enough_index<Count>::type;


    /*  =======================  最快整数类型  ======================================   */


    template <typename IntType>
    struct fast_index {};


    template <typename IntType>
    using fast_index_t = typename fast_index<IntType>::type;


    template <>
    struct fast_index<uint8_t> {
        using type = uint_fast8_t;
    };


    template <>
    struct fast_index<uint16_t> {
        using type = uint_fast16_t;
    };


    template <>
    struct fast_index<uint32_t> {
        using type = uint_fast32_t;
    };


    template <>
    struct fast_index<uint64_t> {
        using type = uint_fast64_t;
    };


    template <size_t Size>
    struct enough_and_fast_index {
        using type = fast_index_t<enough_index_t<Size>>;
    };


    template <size_t Size>
    using enough_and_fast_index_type = typename enough_and_fast_index<Size>::type;


    template <size_t Count>
    struct enough_and_fast_bit_flag {
        using type = fast_index_t<enough_bit_flag_t<Count>>;
    };


    template <size_t Count>
    using enough_and_fast_bit_flag_t = typename enough_and_fast_bit_flag<Count>::type;


    /*  =======================  整数转换工具  ======================================   */


    template <typename T>
    struct to_signed {};


    template <>
    struct to_signed<uint8_t> {
        using type = int8_t;
    };


    template <>
    struct to_signed<uint16_t> {
        using type = int16_t;
    };


    template <>
    struct to_signed<uint32_t> {
        using type = int32_t;
    };


    template <>
    struct to_signed<uint64_t> {
        using type = int64_t;
    };


    template <typename T>
    using to_signed_t = typename to_signed<T>::type;


    template <typename T>
    constexpr bool is_not_unsigned_v = !std::is_unsigned_v<T>;
}  // namespace data_basic
